package org.boxsql.templateEngine;

import static org.boxsql.cache.CacheDecorator.addToCache;
import static org.boxsql.cache.CacheDecorator.getCachedValue;
import static org.boxsql.cache.CacheDecorator.isCached;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.boxsql.cache.GlobalSettings;
import org.boxsql.ioEngine.IOEngine;
import org.boxsql.logEngine.LogEngine;

public class TemplateEngine {

	private String pathBase;

	private List<String> simpleParameters;

	private Map<String, String> complexParams;

	public TemplateEngine() {
		pathBase = GlobalSettings.pathBase;
	}

	public String getSQLQuery(final String fileName, final Object obj) {
		String newSQL;

		if (isCached(fileName)) {
			newSQL = getCachedValue(fileName);
		} else {
			newSQL = IOEngine.readResourceFile(this.getPathBase() + "/"
					+ fileName);
			addToCache(fileName, newSQL);
		}

		// If obj is not null, set params based on ObjectValues.
		if (obj != null)
			setObjectValues(obj);

		// Find and replace for real values, all '#' parameters.
		if (simpleParameters != null && !simpleParameters.isEmpty()) {
			for (int i = 0; i < simpleParameters.size(); i++) {
				newSQL = newSQL.replaceFirst("#", simpleParameters.get(i)
						.toString());
			}
			simpleParameters.clear();
		}

		if (complexParams != null && !complexParams.isEmpty()) {

			if (!complexParams.isEmpty()) {
				for (String key : complexParams.keySet()) {
					newSQL = newSQL.replaceAll(key, complexParams.get(key)
							.replace("$", "\\$"));
				}
			}
		}

		final StringBuffer query = new StringBuffer();
		query.append(newSQL);

		if (GlobalSettings.logActive) {
			new LogEngine().logMessage(query.toString(),
					GlobalSettings.logPath, "INFO");
		}

		return query.toString().trim();
	}

	public String getSQLQueryAutomatic(String sql, final Object obj) {

		// If obj is not null, set params based on ObjectValues.
		if (obj != null)
			setObjectValues(obj);

		if (complexParams != null && !complexParams.isEmpty()) {

			if (!complexParams.isEmpty()) {
				for (String key : complexParams.keySet()) {
					sql = sql.replaceAll(key, complexParams.get(key));
				}
			}
		}

		final StringBuffer query = new StringBuffer();
		query.append(sql);

		if (GlobalSettings.logActive) {
			new LogEngine().logMessage(query.toString(),
					GlobalSettings.logPath, "INFO");
		}

		return query.toString().trim();
	}

	private void setObjectValues(final Object obj) {
		final Method[] metods = obj.getClass().getDeclaredMethods();
		for (final Method element : metods) {
			try {
				if (element.getName().startsWith("get")
						|| element.getName().startsWith("is")) {

					String value = String.valueOf(element.invoke(obj,
							new Object[] {}));

					if ("boolean".equals(element.getReturnType().getName())
							|| "java.lang.Boolean".equals(element
									.getReturnType().getName())) {
						if ("true".equalsIgnoreCase(value)) {
							value = "1";
						} else {
							value = "0";
						}

					}
					int prefixLength = element.getName().startsWith("is") ? 2
							: 3;
					this.setComplexParameter(element.getName().substring(
							prefixLength).toUpperCase(), value);

				}
			} catch (final IllegalArgumentException ex) {
				if (GlobalSettings.logActive)
					new LogEngine().logMessage(ex.getStackTrace().toString(),
							GlobalSettings.logPath, "ERROR");
				throw new RuntimeException(ex);
			} catch (final InvocationTargetException ex) {
				if (GlobalSettings.logActive)
					new LogEngine().logMessage(ex.getStackTrace().toString(),
							GlobalSettings.logPath, "ERROR");
				throw new RuntimeException(ex);
			} catch (final IllegalAccessException ex) {
				if (GlobalSettings.logActive)
					new LogEngine().logMessage(ex.getStackTrace().toString(),
							GlobalSettings.logPath, "ERROR");
				throw new RuntimeException(ex);
			}
		}

	}

	public void setSimpleParameter(final String value) {
		if (simpleParameters == null)
			simpleParameters = new ArrayList<String>();
		simpleParameters.add(value);
	}

	public void setComplexParameter(final String key, final String value) {
		if (complexParams == null)
			complexParams = new HashMap<String, String>();
		complexParams.put(":" + key, value);
	}

	public final String getPathBase() {
		return pathBase;
	}
}
